﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Animation;

namespace FSM_Framework.StateMachine
{
    //condition ：自动迁移， operation：用户动作， action：迁移行为； state：状态
    public class StateMachine<TOperation, TState> where TState : IEquatable<TState>
    {

        public StateMachine(TState initState)
        {
            this.State = initState;
            _autoTransitionListener = new AutoTransitionListener<TOperation, TState>(this);
        }

        private AutoTransitionListener<TOperation, TState> _autoTransitionListener { get; }

        public void RegisterCondition(TState state, AutoTransitionCondition<TState> condition)
        {
            if (this.StateTransTables.ContainsKey(state) == false)
            {
                StateTransTables[state] = new TransitionTable<TOperation, TState>();

            }
            StateTransTables[state].AutoTransitions.Add(condition);

        }

        internal Dictionary<TState, TransitionTable<TOperation, TState>> StateTransTables { get; } = new Dictionary<TState, TransitionTable<TOperation, TState>>();


        //todo:condition和operation迁移到同一个状态，是否共用一种Action？目前是分离的
        // private Dictionary<State, Dictionary<string, TransitionAction>> _transitionActions;

        public void RegisterOperation(TState originalState, TOperation operation, TState targetState)
        {
            if (StateTransTables.ContainsKey(originalState) == false)
            {
                StateTransTables[originalState] = new TransitionTable<TOperation, TState>();
            }

            StateTransTables[originalState].OperationTransDictionary.Add(operation, targetState);

        }


        public event EventHandler<StateChangedArgs<TState>> StateChanged;

        private bool CanTransient(TState state, TOperation operation)
        {
            var currentState = state;
            var transientTDictionary = StateTransTables[currentState];
            return transientTDictionary.OperationTransDictionary.ContainsKey(operation);
        }
        private TState GetNextStateByOperation(TOperation operation)
        {
            if (CanTransient(State, operation) == false)
                throw new InvalidOperationException("不能执行此操作");
            var transientTDictionary = StateTransTables[State];
            var nextState = transientTDictionary.OperationTransDictionary[operation];
            return nextState;
        }
        private void OnStateChanged(TState stateBefore, TState state)
        {
            StateChanged?.Invoke(this, new StateChangedArgs<TState>() { StateBefore = stateBefore, State = state });
        }
        public TState State { get; set; }
        private bool _isBusy = false;
        /// <summary>
        /// 正在执行迁移动作
        /// </summary>
        public bool IsBusy
        {
            get => _isBusy;
            set
            {
                _isBusy = value;
                IsBusyChanged.Invoke(this, value);
            }
        }
        public event EventHandler<bool> IsBusyChanged;

        protected internal SequentialScheduler Scheduler = new SequentialScheduler();

        public bool CanExecute(TOperation operation)
        {

            //为了提高效率，分两步
            //1.同步判断，否直接返回false
            //2.true时候,抛到队列，然后返回
            var canExe = () =>
            {
                if (IsBusy || CanTransient(State, operation) == false)
                {
                    return false;
                }
                return true;
            };

            if (canExe.Invoke() == false) return false;
            return true;
            //return (bool)await Scheduler.ExecuteAsyncInternal(p => canExe.Invoke(), null);

        }

        public async Task<bool> ExecuteAsync(TOperation operation, Action action)
        {
            return await ExecuteAsync(operation, new TransitionAction() { Execute = p => action?.Invoke() });
        }

        public async Task<bool> ExecuteAsync(TOperation operation, TransitionAction transitionAction)
        {
            return await ExecuteAsyncInternal(() => CanExecute(operation), () => GetNextStateByOperation(operation), transitionAction);

        }
        internal async Task<bool> ExecuteAsyncInternal(Func<bool> canExe, Func<TState> transToNewState, TransitionAction transitionAction)
        {
            var canExecute = new Func<object, bool>(p =>
            {
                if (!canExe.Invoke()) return false;
                return transitionAction.CanExecute == null || transitionAction.CanExecute.Invoke(p);
            });

            if (!canExecute(transitionAction.Parameter)) return false;
            return (bool)await Scheduler.ExecuteAsyncInternal(param =>
            {
                try
                {


                    if (!canExecute(param)) return false;
                    IsBusy = true;
                    transitionAction?.PreAction?.Invoke(param);
                    transitionAction?.Execute?.Invoke(param);
                    var stateBefore = State;
                    var nextState = transToNewState.Invoke();
                    State = nextState;
                    transitionAction?.PostAction?.Invoke(param);
                    OnStateChanged(stateBefore, nextState);

                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex);
                    return false;
                }
                finally
                {
                    IsBusy = false;
                }

            }, transitionAction.Parameter);

        }


    }


}
